/*
 * ConfigurationHandler.c
 *
 *  Created on: Mar 28, 2012
 *      Author: chk052
 *  Implements: SC-91100
 *
 *  TODO N Support Setvariable if not set, so this can be a lib and the app can set vars after the cli has been processed.
 *  TODO N Support exclusion rules for Variables?
 *  TODO N Support Environment variables
 *  TODO N Support config files.
 */

#include <getopt.h>
#include <stdio.h>
#include <string.h>

#include "ConfigurationHandler.h"
#include "FunctionTracing.h"

#define VARIABLE_TYPE_UNDEFINED '-'
#define VARIABLE_TYPE_BOOLEAN 'b'

struct f_stVariableInformation {
	// TODO N have a var for holding the source of the setting.
	char szName[MAX_LENGT_OF_VARIABLE];
	/** cVariableType
	 * '-' Undefined;
	 * 'c' Character;
	 * 'b' Boolean;
	 * 'i' Integer;
	 * 's' String;
	 */
	char cVariableType;

	int bBooleanValue;

// TODO N support various data type int nIntergerValue = 0;
// have SetInt, SetChar, SetTExt etc. And then have an enum for the types and a var in the structure to store what type the is.
//   e.g. cVariableType := < i | s | c >

};

// If you want to have some pre-programmed variables, then Init needs to call that function.
// TODO N Make this list dynamic.
struct f_stVariableInformation f_hVariableList[MAX_NUMBER_OF_VARIABLES];

/**
 * Provide the index number in f_hVariableList where szVariableName can be found.
 * Search through the f_hVariableList for the szVariableName.
 *
 * @return -1 on not found, otherwise return the index number starting with 0.
 */
int ConfigurationVariableGetUnusedEntry() {
	int nIndex = -1;
	int nCount = 0;

	while ((nIndex == -1) && (nCount < MAX_NUMBER_OF_VARIABLES)) {
		if (f_hVariableList[nCount].cVariableType == VARIABLE_TYPE_UNDEFINED) {
			nIndex = nCount;
		}
		nCount++;
	}

	return (nIndex);
}

/**
 * Analyze the arguments, extract the command line options.
 * Make the extracted configurations available through
 * @see ConfigurationVariableExist
 *
 * @return 0 - ok, -1 on failure.
 */
int ConfigurationHandlerInitialization(int nArgumentCount, char **pArguments) {
	int nReturnCode = 0;

	// printf("III ConfigurationHandlerInitialization(%d,)\n", nArgumentCount);
	// TODO N support config files.
	// TODO N support ENV vars.
	ConfigurationVariableClearHash();

	/** Long options structure.
	 * name, has_arg, flag, val
	 */
	static struct option stLongOptionList[] = { { "ch", 0, 0, 0 }, { "help", 0,
			0, 'h' }, { "tcv", 0, 0, 0 }, { "version", 0, 0, 'v' }, { 0, 0, 0,
			0 } };
	static char szShortOptions[] = "hv";
	int nLongOptionIndex = 0;
	int cOption;

	while ((nReturnCode == 0) && (cOption = getopt_long(nArgumentCount,
			pArguments, szShortOptions, stLongOptionList, &nLongOptionIndex))
			!= EOF) {
		//printf("III    Inside loop: 0x%X\n", cOption);

		switch (cOption) {
		case 0:
			TRACEF(6,("long option %s\n", stLongOptionList[nLongOptionIndex].name));
			if (!optarg) {
				ConfigurationVariableSetBooleanTrue(
						stLongOptionList[nLongOptionIndex].name);
			} else {
				// TODO V Handle arguments.
				// printf(" with arg %s", optarg);
				// TODO N handle the return code.
			}
			//printf("\n");
			break;
		case 'h':
			ConfigurationVariableSetBooleanTrue(HELP_VARIABLE_NAME);
			break;
		case 'v':
			ConfigurationVariableSetBooleanTrue(VERSION_VARIABLE_NAME);
			break;
		case '?':
			printf("!!! Unknown option: 0x%X (%c)\n", cOption, (char) cOption);
			nReturnCode = -1;
			break;
			/* I do not know how to reach this point.
			 default:
			 nReturnCode = -2;
			 break;
			 */
		} // end switch.

	} // end while.
	return (nReturnCode);
} // configurationhandlerinitialization.


/**
 * Clears the hash list, so it is meaningful to run through the list looking for variable names..
 *
 *
 */
int ConfigurationVariableClearHash() {
	int nReturnCode = 0;

	int nCount = 0;
	for (nCount = 0; nCount < MAX_NUMBER_OF_VARIABLES; nCount++) {
		f_hVariableList[nCount].szName[0] = '\0';
		f_hVariableList[nCount].cVariableType = VARIABLE_TYPE_UNDEFINED; // TODO V put this in an enum.
	}
	return (nReturnCode);
} // end ConfigurationVariableSetBoolean


/**
 * Return logical yes if the given szVariableName exists in the Variable list.
 */
int ConfigurationVariableExist(const char *szVariableName) {
	int nReturnCode = IsValidVariableName(szVariableName);

	if (nReturnCode == 0) {
		//printf(">>> ConfigurationVariableExist(%s)\n", szVariableName);
		int nIndex = ConfigurationVariableLookUpName(szVariableName);
		if (nIndex >= 0) {
			//printf("<<<     found at %d\n", nIndex);
			nReturnCode = 1;
		}
	} else {
		nReturnCode = 0;
	}
	return (nReturnCode);
} // end ConfigurationVariableExist.

/**
 * Provide the index number in f_hVariableList where szVariableName can be found.
 * Search through the f_hVariableList for the szVariableName.
 *
 * @return -1 on not found, otherwise return the index number starting with 0.
 */
int ConfigurationVariableLookUpName(const char *szVariableName) {
	int nIndex = IsValidVariableName(szVariableName);

	int nCount = 0;

	if (nIndex == 0) {
		nIndex = -1;

		while ((nIndex == -1) && (nCount < MAX_NUMBER_OF_VARIABLES)) {
			if (f_hVariableList[nCount].cVariableType
					!= VARIABLE_TYPE_UNDEFINED) {
				// printf("III   ConfigurationVariableLookUpName(%s) at %d : %s\n", szVariableName, nCount, f_hVariableList[nCount].szName);
				if (strncmp(f_hVariableList[nCount].szName, szVariableName,
						MAX_LENGT_OF_VARIABLE) == 0) {
					nIndex = nCount;
				}
			}
			nCount++;
		} // end while
	} // endif

	return (nIndex);
}

/**
 * Set the given value to true.
 *
 * @return 0 - Ok,
 *   -1 - szVariableName was null; Provide a name.
 *   -2 - Name too long; Use a shorter name.
 *   -3 - No room for additional variables;
 */
int ConfigurationVariableSetBooleanTrue(const char *szVariableName) {
	int nReturnCode = 0;
	int nIndex = -1;

	nReturnCode = IsValidVariableName(szVariableName);

	if (nReturnCode == 0) {
		nIndex = ConfigurationVariableLookUpName(szVariableName);
	}
	if (nIndex == -1) {
		nIndex = ConfigurationVariableGetUnusedEntry();
	}
	if (nIndex != -1) {
		f_hVariableList[nIndex].cVariableType = VARIABLE_TYPE_BOOLEAN;
		// strncpy shouldn't be necessary, because there is a guard again too long var, but to be on the safe side.
		strncpy(f_hVariableList[nIndex].szName, szVariableName,
				MAX_LENGT_OF_VARIABLE - 1);
		f_hVariableList[nIndex].szName[MAX_LENGT_OF_VARIABLE - 1] = '\0';
	} else {
		nReturnCode = -3;
	}
	return (nReturnCode);
} // end ConfigurationVariableSetBoolean

/**
 * Validate if the szVariableName meets the pre-conditions.
 *
 * @return 0 - Ok,
 *   -1 - szVariableName was null; Provide a name.
 *   -2 - szVariableName empty.
 *   -3 - Name too long; Use a shorter name.
 *   TODO V Should it also fail if the following is false: [0-9]|[A-Z]|[a-z]|_|-
 *   TODO V Rename to e.g. Validate... since Is.. indicate 0 is false..
 */
int IsValidVariableName(const char *szVariableName) {
	int nReturnCode = 0;
	if (szVariableName == NULL) {
		nReturnCode = -1;
	} else if (strlen(szVariableName) == 0) {
		// The -1, above, is to leave room for the terminating '\0'
		nReturnCode = -2;
	} else if (strlen(szVariableName) >= MAX_LENGT_OF_VARIABLE - 1) {
		// The -1, above, is to leave room for the terminating '\0'
		nReturnCode = -3;
	}
	return (nReturnCode);
} // end IsValidVariableName

